package kb;


import java.util.HashMap;
import entities.concept.AndConcept;
import entities.concept.Concept;
import entities.concept.DomainConceptEQ;
import entities.role.Role;
/**
 * @serial
 * A {@code DescriptionLogicsKBox} that consists of a TBox with axioms and an ABox with assertions 
 * can be serialized.
 * @author j55wu
 *
 */
public class DescriptionLogicsKB implements KB {

	private final TBox tb;
	private final ABox ab;
	//at present we cache f=k computed by queries, also possible to cache all occurrences in ABox
//	private HashMap<Individual, HashMap<RolePath, HashMap<Role, String>>> cachedDomainConcepts;
//	private static long domainCacheHit = 0;

//	public static long getDomainCacheHit() {
//		return domainCacheHit;
//	}
//
//	public void setCachedDomainConceptEQEntry(Individual ind, RolePath rp, DomainConceptEQ<?,?> dc) 
//		throws CacheNodeException{
//		//if rp is used in other optimization, its concept may not be null
//		Concept filler = rp.getConcept();
//		RolePath myrp;
//		myrp = new RolePath(null, rp.getOffset());
//		myrp.getRoles().addAll(rp.getRoles());
//		if(filler != null){
//			//get a new role path for caching, since rp is mutable!
//			if(filler instanceof DomainConceptEQ<?,?>){
//				this.setCachedDomainConceptEQEntry(ind, myrp, (DomainConceptEQ<?, ?>) filler);
//			}
//		}
//		if(this.cachedDomainConcepts == null){
//			this.cachedDomainConcepts = new HashMap<Individual, HashMap<RolePath, HashMap<Role, String>>>();
//		}
//		HashMap<RolePath, HashMap<Role, String>> newsub;
//		if(this.cachedDomainConcepts.containsKey(ind)){
//			newsub = this.cachedDomainConcepts.get(ind);
//		}else{
//			newsub = new HashMap<RolePath, HashMap<Role, String>>();
//		}			
//		setSubCachedDomainConceptEQEntry(newsub, myrp, dc);
//		this.cachedDomainConcepts.put(ind, newsub);
//		//System.err.println(displayCache());
//	}
//	
//	public String displayCache(){
//		String s="===content===\n";
//		for(Entry<Individual, HashMap<RolePath, HashMap<Role, String>>> e:this.cachedDomainConcepts.entrySet()){
//			s += e.getKey() +": ";
//			for(Entry<RolePath, HashMap<Role, String>> ee:e.getValue().entrySet()){
//				s += "["+ee.getKey()+" -> "+ee.getValue()+"]";
//			}
//			s+="\n";
//		}
//		return s;
//	}
//	
//	public HashMap<Individual, HashMap<RolePath, HashMap<Role, String>>> getCachedDCEntries(){
//		return this.cachedDomainConcepts;
//	}
//	
//	private void setSubCachedDomainConceptEQEntry(HashMap<RolePath, HashMap<Role, String>> map,
//			RolePath rp, DomainConceptEQ<?,?> dc) throws CacheNodeException{
//		HashMap<Role, String> fkmap;
//		if(map.containsKey(rp)){
//			fkmap = map.get(rp);
//		}else{
//			fkmap = new HashMap<Role, String>();
//		}
//		if(fkmap.containsKey(dc.getRealFeature()) ){
//			if(dc.getRealValue().equals(fkmap.get(dc.getRealFeature()))){
//				if(rp.getOffset() == 0){
//					//no role path, f=1 and f=2 : error
//					throw new CacheNodeException("Value already exists for "+dc.toString());
//				}
//				//the same thing, not cached
//				return;
//			}
//			
//		}else{
//			fkmap.put((Role)dc.getRealFeature(), (String)dc.getRealValue());
//			map.put(rp, fkmap);
////			System.err.println("Cache -- "
////					+": "+rp.toString() +". "+dc.toString());
//		}
//	}
//	
//	public HashMap<Role, String> getCachedDomainConceptEQEntry(Individual ind, RolePath rp){
//		if(this.cachedDomainConcepts == null){
//			return null;
//		}
//		if(this.cachedDomainConcepts.containsKey(ind)){
//			HashMap<RolePath, HashMap<Role, String>> sub = this.cachedDomainConcepts.get(ind);
//			RolePath myrp;
//			myrp = new RolePath(null, rp.getOffset());
//			myrp.getRoles().addAll(rp.getRoles());
//			if(sub.containsKey(myrp)){
//				 return sub.get(myrp);	
//			}
//		}
//		return null;
//	}
//	
//	public DomainConceptEQ<?,?> getCachedDomainConceptEQEntry(Individual ind, RolePath rp, Role f){
//		if(this.cachedDomainConcepts == null){
//			return null;
//		}
//		if(this.cachedDomainConcepts.containsKey(ind)){
//			HashMap<RolePath, HashMap<Role, String>> sub = this.cachedDomainConcepts.get(ind);
//			RolePath myrp;
//			myrp = new RolePath(null, rp.getOffset());
//			myrp.getRoles().addAll(rp.getRoles());
//			if(sub.containsKey(myrp)){
//				HashMap<Role, String> dcs = sub.get(myrp);
////				if(rp.getConcept() != null){
////					if(! this.decideCacheSoundness(rp.getConcept(), dcs)){
////						//not qualified to obtain the cache: otherwise unsound results
////						return null;
////					}
////				}			
//				if(dcs.containsKey(f)){
//					domainCacheHit ++;
//					return new DomainConceptEQ<Role,String>(f, dcs.get(f));
//				}
//			}
//		}
//		return null;
//	}
	
	/**
	 * Given a rp: Rp.C, looking for f?, we do the following.
	 * Analyze C, see if C is purely made of (f1=k1 AND f2=k2 AND ...) 
	 * such that all the f_i = k_i are in the cache. 
	 * 
	 * Note C is produced by Pd, i.e., C can only be of the form:
	 * C?, f?, Pd1 AND Pd2, Pd (where Rp is changed by Exists.R)
	 *  Later cache can further optimize such situations in case C is cached,
	 *  instead of only f_i=k_i.
	 * @return
	 */
	public boolean decideCacheSoundness(Concept C, HashMap<Role, String> fks){
		boolean pure = false;
		if(C == null){
			return true;
		}
		if(C instanceof DomainConceptEQ<?,?>){
			DomainConceptEQ<?,?> dc = (DomainConceptEQ<?, ?>) C;
			if(fks.containsKey(dc.getRealFeature())){
				return true;
			}
		}else if(C instanceof AndConcept){
			for(Concept pc:((AndConcept)C).getConceptSet()){
				pure &= this.decideCacheSoundness(pc, fks);
				if(!pure) return false;
			}
		}
		return pure;
	}
	
	public static DescriptionLogicsKB newNullKB(){
		return new DescriptionLogicsKB(null, null);
	}
	
	public static DescriptionLogicsKB newKBInstance(TBox tb, ABox ab){
		return new DescriptionLogicsKB(tb, ab);
	}
	
	private DescriptionLogicsKB(TBox tb, ABox ab) {
		super();
		this.tb = tb;
		this.ab = ab;
	}
	
	public TBox getTbox() {
		return tb;
	}
	public ABox getAbox() {
		return ab;
	}
	
	@Override
	public String toString(){
		return this.tb.toString() +"\n\n"+this.ab.toString();
	}
	
}
